/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/commercial
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may use this file in compliance with the License.
 * Alternatively, if you can't copy with the License, namely if you do not
 * want to provide source code of your modifications to this file, you may
 * negotiate a commercial licence with the initial developer.
 * You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Brano Kemen
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef __COID_SERVER_LOGCOID__HEADER_FILE__
#define __COID_SERVER_LOGCOID__HEADER_FILE__

/*
  Log class serves for operation logging for the COID server and the services running under the server.
  Automatically logs time of events, and allows to setup a pointer to the log buffer which can return log
  starting from specified time.
*/
#include <time.h>

#include "coid/comm/comm.h"
#include "coid/comm/str.h"
#include "coid/comm/segarray.h"
#include "coid/comm/binstream/binstreambuf.h"
#include "coid/comm/binstream/filestream.h"
#include "coid/comm/version.h"

#include "coid/svc/logstream.h"
#include "coid/svc/cmdin.h"
#include "filter.h"


namespace coid {


class logstream_ext : public logstream
{
public:
    void flush();
};

////////////////////////////////////////////////////////////////////////////////
///Log class with multiple connectors which post events
class SVCLOG
{
public:

    typedef segarray<logstream*,EmptyTail,uints>    SEG_LOG;
    typedef SEG_LOG::ptr                        SEG_LOG_PTR;


    logstream* get_entry( uint id, uchar msgclass, uchar verb, const token& shortname, const char* src )
    {
        GUARDME;

        for( uints p=_cache.size(); p>0; )
        {
            --p;
            if( _cache[p]->is_final() )
            {
                _cache[p]->set( id, msgclass, verb, shortname, src );
                return _cache[p];
            }
        }

        logstream_ext* en = *_cache.add() = new logstream_ext;
        en->set( id, msgclass, verb, shortname, src );
        return en;
    }

    void set_entry( logstream* log )
    {
        GUARDME;

        SEG_LOG_PTR p;
        _log.push(p);

        *p = log;
        log->reset_final();
    }

    ///
    struct logpointer
    {
        SVCLOG&    _log;
        const Filter* _filter;
        uints      _ref;

        void read( dynarray<logstream*>& out, uints max )
        {
            MXGUARD(_log);

            out.reset();
            if( _ref >= _log.msgcount() )
                return;

            SEG_LOG_PTR p;
            _log.get_log_ptr( p, _ref );

            for( ; max>0  &&  p.is_valid(); ++p,--max )
            {
                if( _filter  &&  !_filter->passes(**p) )
                    continue;

                *out.add(1) = *p;
            }

            _ref = p.index();
        }

        uints get_ref() const           { return _ref; }

        logpointer( SVCLOG& log, const Filter* filter, uints ref )
            : _log(log), _filter(filter), _ref(ref)
        {}
    };


    static charstr& print_msg( charstr& buf, const logstream& msg )
    {
        char c;
        switch( msg._cclass )
        {
        case LOGMSG::TYPE_ALERT:    c = 'A';  break;
        case LOGMSG::TYPE_CRIT:     c = 'C';  break;
        case LOGMSG::TYPE_ERROR:    c = 'E';  break;
        case LOGMSG::TYPE_WARNING:  c = 'W';  break;
        case LOGMSG::TYPE_NOTICE:   c = 'n';  break;
        case LOGMSG::TYPE_INFO:     c = 'i';  break;
        // debug
        case LOGMSG::TYPE_DEBUG:    c = 'D';  break;
        case LOGMSG::TYPE_TRACE:    c = 'T';  break;
        // system
        case LOGMSG::TYPE_OPEN:     c = 'o';  break;
        case LOGMSG::TYPE_CLOSE:    c = 'c';  break;
        case LOGMSG::TYPE_TERM:     c = 't';  break;
        case LOGMSG::TYPE_ATTACH:   c = 'a';  break;
        case LOGMSG::TYPE_DETACH:   c = 'd';  break;

        case LOGMSG::TYPE_METHOD:   c = 'm';  break;

        default:                    c = '?';
        }

#ifdef SYSTYPE_MSVC8plus
        struct tm ttm;
        struct tm* t = &ttm;
        localtime_s( t, (const time_t*)&msg._time );
#else
        struct tm *t = localtime( (const time_t*)&msg._time );
#endif

        buf.append_num( 10, t->tm_hour, 2, ALIGN_NUM_RIGHT_FILL_ZEROS );
        buf.append(':');
        buf.append_num( 10, t->tm_min, 2, ALIGN_NUM_RIGHT_FILL_ZEROS );
        buf.append(':');
        buf.append_num( 10, t->tm_sec, 2, ALIGN_NUM_RIGHT_FILL_ZEROS );
        buf.append(' ');

        buf.append( msg._src, 4, ' ' );

        if( msg._obj == UMAX32 )
            buf += "   ?";
        else
            buf.append_num( 10, msg._obj, 4, ALIGN_NUM_RIGHT );
        
        buf.append(' ');
        buf.append( msg._short, 8, ' ' );

        buf.append(' ');
        buf.append(c);
        buf.append(' ');

        token tx = msg.text();

        if( !tx.is_empty() )
            buf += tx;

        return buf;
    }


    void dump()
    {
        if( _log.get_last_segment_id() < 16 )
            return;

        charstr buf;
        fileiostream fio;

        fio.open( "coid.log", "a+b" );

        uint n=8;
        GUARDME;
        for( ; n>0; --n )
        {
            uint ni;
            if( 0 != _log.get_segment_info( 0, 0, &ni, 0 ) ) break;

            SEG_LOG_PTR p;
            _log.get_ptr( 0, p );
            for( uint i=0; i<ni; ++i )
            {
                if( !(*p)->is_final() )  { ni=i;  n=1;  break; }

                buf.reset();
                print_msg( buf, **p );

                buf << "\n";

                fio.xwrite_raw( buf.ptr(), buf.len() );
            }

            fio.flush();
            _log.del( 0, ni );
        }
    }

    
    ///SVCLOG
    SVCLOG()
    {
        _log.set_segsize( 1<<16 );
		_mutex.set_name( "SVCLOG" );
    }

    SEG_LOG_PTR& get_log_ptr( SEG_LOG_PTR& ptr, uints ref ) {
        return _log.get_ptr(ref, ptr);
    }

    // for guards
    operator comm_mutex& ()     { return _mutex; }

    uints msgcount() const      { return _log.size(); }

private:
    SEG_LOG  _log;
    mutable comm_mutex  _mutex;

    dynarray<logstream_ext*>    _cache;

    fileiostream _fio;
};


} // namespace coid

#endif //__COID_SERVER_LOGCOID__HEADER_FILE__
